﻿#include "FITKGraphObject3D.h"

// VTK
#include <vtkObject.h>
#include <vtkProperty.h>
#include <vtkPolyData.h>
#include <vtkUnstructuredGrid.h>

// Graph 
#include "FITKGraphCommons.h"
#include "FITKSetSurfPolyProvider.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

// Graph widget
#include "FITK_Kernel/FITKCore/FITKAbstractGraphWidget.h"

// Global data
#include "FITK_Kernel/FITKCore/FITKDataRepo.h"

// Data
#include "FITK_Kernel/FITKCore/FITKAbstractNDataObject.h"

// Case
#include "FITK_Component/FITKAbaqusData/FITKDataCaseManager.h"
#include "FITK_Component/FITKAbaqusData/FITKDataCase.h"
#include "FITK_Component/FITKAbaqusData/FITKAbaqusData.h"

namespace Exchange
{
    FITKGraphObject3D::FITKGraphObject3D(AbaqusData::FITKDataCase* caseData, Core::FITKAbstractDataObject* dataObj)
        : Comp::FITKGraphObjectVTK(dataObj), m_caseData(caseData)
    {
        // Abstract data object class,
        // sub class of the render window interface data object.

        // If the data  has been deleted, then delete self.
        // Do not use for now.
        // The graph object will be deleted by the graph provider,
        // when the dataObj has been deleted.
        if (dataObj)
        {
            connect(dataObj, &Core::FITKAbstractNDataObject::dataObjectDestoried, this, &FITKGraphObject3D::sig_needToBeDeleted);
        }

        // Save the case id.
        if (caseData)
        {
            m_graphInfo.CaseId = caseData->getDataObjectID();
        }

        // Save the data object id.
        if (dataObj)
        {
            m_graphInfo.DataObjId = dataObj->getDataObjectID();
        }
    }

    FITKGraphObject3D::~FITKGraphObject3D()
    {
        // Delete the additional graph objects.
        deleteObjs(m_addinGraphObjList);

        // Clear the information.
        m_userInfo.clear();

        // Emit the deleted signal.
        emit sig_destroyed(getDataId());
    }

    QString FITKGraphObject3D::IdArrayName()
    {
        return ID_ARRAY;
    }

    void FITKGraphObject3D::DeleteSetGrid(int setId)
    {
        FITKSetSurfPolyProvider::getInstance()->deleteSetGridData(setId);
    }

    void FITKGraphObject3D::DeleteSurfaceGrid(int surfaceId)
    {
        FITKSetSurfPolyProvider::getInstance()->deleteSurfaceGridData(surfaceId);
    }

    Core::FITKAbstractGraphWidget* FITKGraphObject3D::setConstGraphWidget(Core::FITKAbstractGraphWidget* widget)
    {
        Core::FITKAbstractGraphWidget* constWidget = getGraphWidget();
        if (constWidget)
        {
            return constWidget;
        }

        if (!widget)
        {
            return nullptr;
        }

        // Set the widget pointer to NULL if the widget has been destoryed.
        connect(widget, &Core::FITKAbstractGraphWidget::destroyed, this, [=]
        {
            m_constGraphWidget = nullptr;
        }, Qt::UniqueConnection);

        // Save the graph widget for removing actors.
        setGraphWidget(widget);
        m_constGraphWidget = widget;

        return widget;
    }

    bool FITKGraphObject3D::isValid()
    {
        if (!_dataObj)
        {
            return false;
        }

        // Check the data object.
        Core::FITKAbstractDataObject* dataObj = FITKDATAREPO->getDataByID(m_graphInfo.DataObjId);
        if (!dataObj)
        {
            return false;
        }

        // Check the number of the actors.
        return m_actorList.count() != 0;
    }

    void FITKGraphObject3D::setPickable(bool isPickable, int dim)
    {
        // Override in sub-class.
        Q_UNUSED(isPickable);
        Q_UNUSED(dim);
    }

    bool FITKGraphObject3D::isHighlighting()
    {
        return m_isHighlighting;
    }

    bool FITKGraphObject3D::isAdvancedHighlighting()
    {
        return m_isAdvHighlighting;
    }

    bool FITKGraphObject3D::isPreHighlighting()
    {
        return m_isPreHighlighting;
    }

    void FITKGraphObject3D::setColor(QColor color)
    {
        // Override in 'FITKGraphObjectHighlightBase', 'FITKGraphObjectPick' and 'FITKGraphObjectModelHighlighting'.
        Q_UNUSED(color);
    }

    void FITKGraphObject3D::setFITKRender(Comp::FITKGraphRender* render)
    {
        m_fRender = render;
    }

    void FITKGraphObject3D::setRenderLayer(int layer, int layerHighlight)
    {
        // Default 0.
        m_layer = layer;

        m_layerHighlight = (layerHighlight == -1) ? m_layer : layerHighlight;
    }

    int FITKGraphObject3D::getRenderLayer()
    {
        // Default 0, override in sub-class.
        return m_layer;
    }

    void FITKGraphObject3D::setRenderLayerHighlight(int layer)
    {
        // Default 0.
        m_layerHighlight = layer;
    }

    int FITKGraphObject3D::getRenderLayerHighlight()
    {
        // Default 0, override in sub-class.
        return m_layerHighlight;
    }

    void FITKGraphObject3D::initActorProperties(vtkProp* actor, QVariant details)
    {
        // Override if necessary.
        // Set the actor properties.
        Q_UNUSED(actor);
        Q_UNUSED(details);
    }

    void FITKGraphObject3D::preHighlight()
    {
        // Override if necessary.
        // Pre-highlight the graph actors.
        m_isPreHighlighting = true;
    }

    void FITKGraphObject3D::highlight(QColor color)
    {
        Q_UNUSED(color);

        // Override if necessary.
        // Highlight the graph actors.
        m_isHighlighting = true;
    }

    void FITKGraphObject3D::advanceHighlight(QVariant details, QColor color)
    {
        Q_UNUSED(details);
        Q_UNUSED(color);

        // Override if necessary.
        // Highlight the graph actor(s) by the given rule.
        m_isAdvHighlighting = true;
    }

    void FITKGraphObject3D::disHighlight()
    {
        // Override if necessary.
        // Dis-highlight the graph actors.
        m_isHighlighting = false;
        m_isPreHighlighting = false;
        
        // Dis-advance-highlight at the same time.
        disAdvanceHighlight();
    }

    void FITKGraphObject3D::disAdvanceHighlight()
    {
        // Override if necessary.
        m_isAdvHighlighting = false;
    }

    void FITKGraphObject3D::updateVisibility()
    {
        // Override if necessary.
        // Show or hide the graph actors.
        bool visibility = getDataVisibility();
        setVisible(visibility);
    }

    void FITKGraphObject3D::update(bool forceUpdate)
    {
        Q_UNUSED(forceUpdate);

        // Override if necessary.
        for (vtkProp* prop : m_actorList)
        {
            FITKGraphActor* actor = FITKGraphActor::SafeDownCast(prop);
            if (actor)
            {
                actor->update();
            }
        }
    }

    int FITKGraphObject3D::getDataId()
    {
        return m_graphInfo.DataObjId;
    }

    GraphInfo FITKGraphObject3D::getGraphInfo()
    {
        return m_graphInfo;
    }

    void FITKGraphObject3D::updateCameraNormal()
    {
        // Override if necessary.
    }

    void FITKGraphObject3D::updateCameraPosition()
    {
        // Override if necessary.
    }

    void FITKGraphObject3D::updateMouseMove(int* pos)
    {
        // Override if necessary.
        Q_UNUSED(pos);
    }

    void FITKGraphObject3D::updateMousePressed(int* pos)
    {
        // Override if necessary.
        Q_UNUSED(pos);
    }

    //void FITKGraphObject3D::setGraphSimplified(bool flag)
    //{
    //    m_graphSimplified = flag;
    //}

    void FITKGraphObject3D::simplified(bool enabled)
    {
        Q_UNUSED(enabled);
    }

    QList<FITKGraphObject3D*> FITKGraphObject3D::getAddinGraphObjs()
    {
        return m_addinGraphObjList;
    }

    void FITKGraphObject3D::removeAddinGraphObj(FITKGraphObject3D* obj)
    {
        if (!obj)
        {
            return;
        }

        m_addinGraphObjList.removeOne(obj);
    }

    void FITKGraphObject3D::setAddinInfo(QVariant info)
    {
        // Override if neccessary.
        Q_UNUSED(info);
    }

    bool FITKGraphObject3D::isRelatedTo(int dataObjId)
    {
        // Override if neccessary.
        // Whether the 'dataObjId' is the id of graph object's data object,
        // this function will return true.
        if (!_dataObj)
        {
            return false;
        }

        return _dataObj->getDataObjectID() == dataObjId;
    }

    bool FITKGraphObject3D::contains(vtkProp* actor)
    {
        // Override if neccessary.
        if (!actor)
        {
            return false;
        }

        // Chekc if the actor is one of the graph's actors.
        bool isSelf = m_actorList.contains(actor);
        if (isSelf)
        {
            return true;
        }

        // Check if the actor is one of the additional graph's object.
        //@{
        bool isAddin = false;
        for (FITKGraphObject3D* obj : m_addinGraphObjList)
        {
            if (!obj)
            {
                continue;
            }

            isAddin |= obj->contains(actor);
        }

        if (isAddin)
        {
            return true;
        }
        //@}

        return false;
    }

    void FITKGraphObject3D::setInputVariantData(QVariant value, int inputRole)
    {
        if (inputRole < 0 || inputRole == m_defaultOutputRole)
        {
            return;
        }

        m_userInfo[inputRole] = value;
    }

    QVariant FITKGraphObject3D::getOutputVariantData(int outputRole)
    {
        if (!m_userInfo.contains(outputRole) || outputRole < 0)
        {
            return QVariant();
        }

        return m_userInfo[outputRole];
    }

    void FITKGraphObject3D::setUserData(int role, QVariant value)
    {
        // User information cannot set to the input and output role.
        if (role == m_defaultInputRole || role == m_defaultOutputRole)
        {
            return;
        }

        if (role < 0)
        {
            return;
        }

        m_userInfo[role] = value;
    }

    QVariant FITKGraphObject3D::getUserData(int role)
    {
        if (!m_userInfo.contains(role) && role >= 0)
        {
            return QVariant();
        }

        return m_userInfo[role];
    }

    void FITKGraphObject3D::clearCache()
    {
        // Override if neccessary.
    }

    void FITKGraphObject3D::updateActors()
    {
        // Modify all actors.
        for (vtkProp* prop : m_actorList)
        {
            prop->Modified();
        }
    }

    bool FITKGraphObject3D::getDataVisibility()
    {
        // Check the data object.
        if (!_dataObj)
        {
            return false;
        }

        // Get the visibility from the data object.
        return _dataObj->isEnable();
    }

    bool FITKGraphObject3D::getParentDataVisibility(int id)
    {
        // Check the model id.
        if (id == -1)
        {
            return false;
        }

        // Check the model data.
        Core::FITKAbstractDataObject* parent = Core::FITKDataRepo::getInstance()->getDataByID(id);
        if (!parent)
        {
            return false;
        }

        return parent->isEnable();
    }

    void FITKGraphObject3D::setVisible(bool visibility)
    {
        // Default set all props managed by this graph object visible or invisible.
        for (vtkProp* prop : m_actorList)
        {
            if (prop)
            {
                prop->SetVisibility(visibility);
            }
        }
    }

    void FITKGraphObject3D::setLightPropertiesForLine(FITKGraphActor* fActor)
    {
        if (!fActor)
        {
            return;
        }

        // Line's light properties.
        //@{
//#if VTK_MAJOR_VERSION >= 9
//        fActor->GetProperty()->SetRoughness(1.);
//#endif
        fActor->GetProperty()->SetAmbient(0.5);
        fActor->GetProperty()->SetDiffuse(1.);
        fActor->GetProperty()->SetSpecular(0.7);
        fActor->GetProperty()->SetSpecularPower(64.);

        //fActor->GetProperty()->SetAmbient(0.2);
        //fActor->GetProperty()->SetDiffuse(1.);
        //fActor->GetProperty()->SetSpecular(0.7);
        //fActor->GetProperty()->SetSpecularPower(64.);
        //@}
    }

    void FITKGraphObject3D::setLightPropertiesForSurface(FITKGraphActor* fActor)
    {
        if (!fActor)
        {
            return;
        }

        // Surface's light properties.
        //@{
//#if VTK_MAJOR_VERSION >= 9
//        fActor->GetProperty()->SetRoughness(0.2);
//#endif
        fActor->GetProperty()->SetAmbient(0.2);
        fActor->GetProperty()->SetDiffuse(0.8);
        fActor->GetProperty()->SetSpecular(0.2);
        fActor->GetProperty()->SetSpecularPower(64.);
        //@}
    }

    bool FITKGraphObject3D::test(QVariant& details)
    {
        // Nothing to do here.
        Q_UNUSED(details);
        return true;
    }

    //char* FITKGraphObject3D::QStringToCharA(QString str)
    //{
    //    return str.toUtf8().data();
    //}
}   // namespace Exchange